package cn.demoncat.util.lang;

import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.thread.LoopThread;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.util.concurrent.*;

/**
 * 线程工具
 * 
 * @author 延晓磊
 *
 * @since 2017年10月12日
 */
public final class ThreadUtil {
	

	/**
	 * 中断当前线程（自我中断）
	 * <pre>
	 * 功能：
	 * 当前线程自我中断，即标记"Thread.currentThread().isInterrupted()"为true
	 * 
	 * 注意：
	 * 1、线程中断，并不是终止线程，而是设置"线程.isInterrupted()"为true，这样在线程执行Thread系操作（sleep,wait等）时就会抛出InterruptedException
	 * 2、线程catch InterruptedException时，会清除中断标记，即"线程.isInterrupted()"为false，这样下线执行Thread系操作时就不会再抛出
	 * 3、线程在启动前、销毁后，即isAlive()为false时，"线程.isInterrupted()"始终为false，即使是执行了interrupt()；即isAlive()为false时执行interrupt()无效
	 * 
	 * 用法：
	 * 1、线程中断，相当于线程间的通信标记，用于通知其它线程终止运行
	 * 2、线程收到 InterruptedException时，表示其它线程终止了它，这时应执行线程退出的操作，例如run()中有while(true)时break
	 * 3、线程的任务如果是"run().while(true)"，并且有可能被interrupt()，则每次循环前应先判断"Thread.currentThread().isInterrupted()"为true则停止，这样中断才有效果
	 * 
	 * 外部中断：
	 * 1、A线程执行"B线程.interrupt()"
	 * 2、A线程调用"B线程.isInterrupted()"为true
	 * 3、B线程执行Thread系操作，抛出InterruptedException，同时清除中断标记，即"线程.isInterrupted()"为false
	 * 4、A线程调用"B线程.isInterrupted()"为false
	 * 
	 * 自我中断：
	 * 1、A线程执行"Thread.currentThread().interrupt()"
	 * 2、A线程调用"Thread.currentThread().isInterrupted()"为true
	 * 3、A线程执行Thread系操作，抛出InterruptedException，同时清除中断标记，即"线程.isInterrupted()"为false
	 * 4、A线程调用"Thread.currentThread().isInterrupted()"为false
	 * 
	 * </pre>
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static void interrupt() {
		Thread.currentThread().interrupt();
	}
	
	/**
	 * 判断当前线程是否为中断状态
	 * 
	 * 1、线程在被执行interrupt()后，isInterrupted()为true，即标记为中断状态，但并没有终止，需要通过编码自定义处理
	 * 2、线程的任务如果是"run().while(true)"，并且有可能被interrupt()，则每次循环前应先判断"isInterrupted() == true"则停止，以便接收其它线程的中断信号
	 * 3、线程在启动前、销毁后，即isAlive()为false时，isInterrupted()始终为false，即使是执行了interrupt()
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static boolean isInterrupted() {
		return Thread.currentThread().isInterrupted();
	}

	/**
	 * 睡眠线程
	 * 
	 * @param seconds 秒数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static void sleep(long seconds){
		try {
			TimeUnit.SECONDS.sleep(seconds);
		} catch (InterruptedException e) {
			// 自我中断
			ThreadUtil.interrupt();
		}
	}
	
	/**
	 * 睡眠线程
	 * 
	 * @param timeout 毫秒
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static void sleepMillis(long timeout){
		try {
			TimeUnit.MILLISECONDS.sleep(timeout);
		} catch (InterruptedException e1) {
			// 自我中断
			ThreadUtil.interrupt();
		}
	}
	
	/**
	 * 同步等待子线程执行
	 * 
	 * @param thread	子线程
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static void join(Thread thread) {
		try {
			// 同步等待子线程
			thread.join();
		} catch (InterruptedException e) {
			// 自我中断
			ThreadUtil.interrupt();
			// 中断子线程
			thread.interrupt();
		}
	}
	
	/**
	 * 同步等待子线程执行
	 * 
	 * @param thread	子线程	
	 * @param timeout	等待毫秒
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月12日
	 */
	public static void join(Thread thread, long timeout) {
		try {
			// 同步等待子线程
			thread.join(timeout);
		} catch (InterruptedException e) {
			// 自我中断
			ThreadUtil.interrupt();
			// 中断子线程
			thread.interrupt();
		}
	}

	/**
	 * 创建任务调度器
	 *
	 * # 场景：spring-task
	 * 1、在 @SpringBootApplication 或 @Configuration 类中声明方法返回
	 * 2、注解方法 @Bean("taskScheduler")，将TaskScheduler声明为Bean
	 * 3、注解方法 @Primary，在项目中有多个线程池时使用以本TaskScheduler
	 * 4、编写任务方法，注解@Scheduled设置定时执行
	 *
	 * @param poolSize	线程池容量
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2018年5月14日
	 */
	public static ThreadPoolTaskScheduler buildScheduler(int poolSize) {
		ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
		taskScheduler.setPoolSize(poolSize);
		taskScheduler.setRemoveOnCancelPolicy(true);
		taskScheduler.setThreadNamePrefix("TaskScheduler-");
		return taskScheduler;
	}

	/**
	 * 创建任务执行器
	 *
	 * 说明：包装ThreadPoolExecutor，可以调用executor.getThreadPoolExecutor()获取
	 *
	 * 容量：0 > core > queue > max > main
	 *
	 * 场景：复用线程池、长期异步操作
	 * 场景：使用CompletableFuture执行异步操作，支持并行/串行
	 * 场景：声明Spring Executor
	 * 操作：关闭 shutdown(不接受新任务，等待已提交任务完成时关闭，非阻塞)，提交任务 submit(可返回Future)、execute(无返回值)
	 *
	 * @param threadName	线程名称前缀
	 * @param corePoolSize	核心线程数：常驻线程，超出且queue满时创建新线程
	 * @param maxPoolSize	最大线程数：超出时不再创建新线程，由调用者执行任务
	 * @param queueCapacity	任务队列容量：核心线程数用满后，新任务会放进queue里，如果queue也占满后，线程池就会创建新线程来处理此任务
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2018年5月14日
	 */
	public static ThreadPoolTaskExecutor buildExecutor(String threadName, int corePoolSize, int maxPoolSize, int queueCapacity) {
		// 创建线程池
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		// 核心线程数：常驻线程。初始0个线程，有任务时创建线程；达到coreSize以后，将新任务放到queue中，而不再创建线程
		executor.setCorePoolSize(corePoolSize);
		// 最大线程数：超出时不再创建新线程，由调用者执行任务（拒绝策略）
		executor.setMaxPoolSize(maxPoolSize);
		// 任务队列容量：核心线程数用满后，新任务会放进queue里；如果queue占满，就会创建新线程来处理任务，达到maxSize后停止
		executor.setQueueCapacity(queueCapacity);
		// 线程名称前缀
		executor.setThreadNamePrefix(StringUtil.toDefault(threadName, "TaskExecutor") + StringConstant.BAR);
		// 拒绝策略-线程数达到最大线程数的处理（0 > core > queue > max > ?）
		// ABORT：抛出TaskRejectedException异常（默认）
		// DISCARD：丢弃任务
		// DISCARD_OLDEST：丢弃queue中最旧的任务，尝试执行新任务
		// CALLER_RUNS：不创建新线程执行，而是由调用者所在的线程来执行
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		// 初始化线程池
		executor.initialize();
		return executor;
	}

	/**
	 * 创建任务执行器-单线程
	 *
	 * 场景：单线程执行任务、临时异步操作
	 * 操作：关闭 shutdown(不接受新任务，等待已提交任务完成时关闭，非阻塞)，提交任务 submit(可返回Future)、execute(无返回值)
	 *
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年07月27日
	 */
	public static ExecutorService buildExecutor(){
		return Executors.newSingleThreadExecutor();
	}

	/**
	 * 创建任务执行器-多线程
	 *
	 * 场景：固定线程执行任务、临时异步操作
	 * 操作：关闭 shutdown(不接受新任务，阻塞等待已提交任务完成时关闭，非阻塞)，提交任务 submit(可返回Future)、execute(无返回值)
	 *
	 * @param size 线程数
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年07月27日
	 */
	public static ExecutorService buildExecutor(int size){
		return Executors.newFixedThreadPool(size);
	}

	/**
	 * 创建（并启动）循环线程
	 *
	 * @param name                  线程名称
	 * @param executeSleepSeconds   每次循环执行后的休眠秒数
	 * @param exceptionSleepSeconds 每次抛异常后的休眠秒数
	 * @param runnable              执行任务
	 *
	 * @return 循环线程控制器
	 *
	 * @author 延晓磊
	 * @since 2021年02月23日
	 */
	public static LoopThread loop(String name, int executeSleepSeconds, int exceptionSleepSeconds, Runnable runnable){
		return LoopThread.build(name, executeSleepSeconds, exceptionSleepSeconds, runnable);
	}

	/**
	 * 闭锁/倒数计数器（通过线程池执行任务count次）
	 *
	 * 场景：主线程让步，等待N个子线程执行完成后再执行（调用 countDownLatch 后阻塞等待，可以在调用后关闭executor线程池）
	 *
	 * @param count		计数
	 * @param executor	线程池
	 * @param runnable  执行任务
	 *
	 * @author 延晓磊
	 * @since 2021年02月23日
	 */
	public static void countDownLatch(int count, Executor executor, Runnable runnable){
		// 创建发射器
		CountDownLatch latch = new CountDownLatch(count);
		// 执行异步操作
		for (int i = 0; i < count; i++) {
			executor.execute(() -> {
				// 执行任务
				runnable.run();
				// 报告/计数
				latch.countDown();
			});
		}
		// 等待发射
		try {
			latch.await();
		} catch (InterruptedException e) {
			// 自我中断
			ThreadUtil.interrupt();
		}
	}

	/**
	 * 闭锁/倒数计数器（通过线程池执行任务count次）
	 *
	 * 场景：主线程让步，通过poolSize大小的线程池执行count次后再执行（调用 countDownLatch 后阻塞等待；自动创建和关闭线程池）
	 *
	 * @param threadName	线程名称前缀
	 * @param poolSize		线程数/并发数
	 * @param count			计数/执行次数
	 * @param runnable  	执行任务
	 *
	 * @author 延晓磊
	 * @since 2021年02月23日
	 */
	public static void countDownLatch(String threadName, int poolSize, int count, Runnable runnable){
		// 创建线程池
		ThreadPoolTaskExecutor executor = ThreadUtil.buildExecutor(threadName, poolSize, poolSize, count);
		// 创建倒数计数器 > 执行任务
		ThreadUtil.countDownLatch(count, executor, runnable);
		// 关闭线程池
		executor.shutdown();
	}

	/**
	 * 循环栅栏/循环计数器/障碍器
	 *
	 * 场景：每当N个子线程执行完成后，主线程执行一次（调用 cyclicBarrier 后不会阻塞等待，在调用后关闭executor线程池将终止循环任务）
	 *
	 * @param count		计数
	 * @param total		总执行数（必须是count的整数倍）
	 * @param executor	线程池
	 * @param runnable  执行任务
	 * @param afterCyclic	每次循环执行的操作（可以为null；每当runnable执行count次后执行1次）
	 *
	 */
	public static void cyclicBarrier(int count, int total, Executor executor, Runnable runnable, Runnable afterCyclic){
		// 判断执行次数
		if (total % count != 0) {
			throw new ParamRuntimeException("总执行数【"+total+"】必须是循环计数【"+count+"】的整数倍");
		}
		// 创建循环栅栏
		CyclicBarrier barrier = new CyclicBarrier(count, afterCyclic);
		// 执行异步操作
		for (int i = 0; i < total; i++) {
			executor.execute(() -> {
				// 执行任务
				runnable.run();
				// 报告/计数
				try {
					barrier.await();
				} catch (InterruptedException | BrokenBarrierException e) {
					// 自我中断
					ThreadUtil.interrupt();
				}
			});
		}
	}

}
